package org.zoomdev.zoom.dao.impl;

import org.apache.commons.lang3.StringUtils;
import org.zoomdev.zoom.common.filter.Filter;
import org.zoomdev.zoom.common.utils.CollectionUtils;
import org.zoomdev.zoom.dao.DaoException;
import org.zoomdev.zoom.dao.Entity;
import org.zoomdev.zoom.dao.EntityField;
import org.zoomdev.zoom.dao.adapters.StatementAdapter;
import org.zoomdev.zoom.dao.auto.AutoField;
import org.zoomdev.zoom.dao.driver.SqlDriver;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

public final class EntitySqlUtils {

    /**
     * 参数顺序
     * <p>
     * ConnectionHolder
     * SimpleSqlBuilder
     * Driver
     * Entity
     * data,
     * Other properties
     */


    public static final char QM = '?';  //Question Mark
    public static final char COMMA = ',';  //comma
    static final Pattern TABLE_AND_COLUMN_PATTERN = Pattern.compile("[a-zA-Z0-9_]+[\\s]*\\.[\\s]*[a-zA-Z0-9_]+|[a-zA-Z0-9_]+");


    public static <T> void buildInsertOrUpdateForMysql(
            StringBuilder sql,
            List<Object> values,
            List<StatementAdapter> insertFields,
            SqlDriver driver,
            Entity entity,
            T data,
            Filter<EntityField> filter,
            boolean ignoreNull,
            String[] uniKeys

    ) {

        sql.append("INSERT INTO ")
                .append(entity.getTable())
                .append(" (");
        boolean first = true;
        EntityField[] fields = entity.getEntityFields();
        int index = 0;
        Object value;
        String[] placeHolder = new String[fields.length * 2];
        for (EntityField entityField : fields) {
            //插入数据,如果有忽略掉其他判断

            if (filter == null || filter.accept(entityField)) {
                value = entityField.get(data);
                if (value == null && ignoreNull) {
                    continue;
                }
                if (first) {
                    first = false;
                } else {
                    sql.append(COMMA);
                }
                appendValue(values, value, insertFields, entityField, sql,
                        placeHolder, index, driver);
                ++index;
            }

        }

        sql.append(") VALUES (").append(StringUtils.repeat("?", ",", index)).append(") ON DUPLICATE KEY UPDATE ");
        first = true;
        Set<String> keySet = new HashSet<String>();
        Collections.addAll(keySet, uniKeys);

        for (EntityField entityField : fields) {
            String key = entityField.getFieldName();
            //插入数据,如果有忽略掉其他判断
            if (keySet.contains(key)) {
                continue;
            }
            if (filter == null || filter.accept(entityField)) {
                if (first) {
                    first = false;
                } else {
                    sql.append(',');
                }
                sql.append(driver.protectColumn(entityField.getColumnName())).append("=?");
                value = entityField.get(data);
                insertFields.add(entityField);
                values.add(value);

                ++index;
            }

        }


    }

    private static void appendValue(List<Object> values, Object value,
                                    List<StatementAdapter> insertFields,
                                    EntityField entityField,
                                    StringBuilder sql,
                                    String[] placeHolder,
                                    int index,
                                    SqlDriver driver) {
        validateValue(entityField, value);
        values.add(value);
        insertFields.add(entityField);
        driver.protectColumn(sql, entityField.getColumnName());
        placeHolder[index] = "?";
    }

    protected static void validateValue(EntityField field, Object value) {
//        try {
//            for (Validator validator : field.getValidators()) {
//                validator.validate(value);
//            }
//        } catch (ValidatorException e) {
//            e.setValue(value);
//            String message = null;
//            switch (e.getType()) {
//                case ValidatorException.CAST:
//                    message = String.format("数据库字段%s格式错误,需要的格式为%s,而实际为%s:%s",
//                            field.getColumnMeta().getName(),
//                            field.getColumnMeta().getDataType(),
//                            value.getClass(),
//                            value);
//                    break;
//                case ValidatorException.LENGTH:
//                    message = String.format("数据库字段%s,数据%s长度过长",
//                            field.getColumnMeta().getName(),
//                            value);
//                    break;
//                case ValidatorException.NULL:
//                    message = String.format("数据库字段%s不能为空", field.getColumnMeta().getName());
//                    break;
//            }
//            e.setMessage(message);
//            throw e;
//        }

    }

    public static <T> void buildInsertIgnoreDuplicated(
            SimpleSqlBuilder builder,
            SqlDriver driver,
            Entity entity,
            T data,
            Filter<EntityField> filter,
            boolean ignoreNull,
            String[] keys) {
        StringBuilder sql = builder.sql;
        List<StatementAdapter> insertFields = builder.adapters;
        List<Object> values = builder.values;
        EntityField[] fields = entity.getEntityFields();


        sql.append("INSERT IGNORE INTO ").append(
                driver.protectTable(entity.getTable())
        ).append(" (");
        boolean first = true;
        int index = 0;
        String[] placeHolder = new String[fields.length];
        // 计算占位符是什么，有可能是? ,也有可能是如 TO_DATE('YYYYMMDD',?)之类的函数调用
        // 也有可能直接是一个值，不需要占位符，如XXX.nextval(),有占位符，则需要值，没有占位符，则不需要值
        for (EntityField entityField : fields) {
            //插入数据,如果有忽略掉其他判断
            AutoField autoField = entityField.getAutoField();
            Object value;
            if (autoField != null) {
                String placeHolderValue;
                if ((placeHolderValue = autoField.getInsertPlaceHolder(data, entityField)) != null) {
                    placeHolder[index] = placeHolderValue;
                } else {
                    // 都没有？ 不需要处理
                    continue;
                }
            } else {
                if (filter == null || filter.accept(entityField)) {
                    value = entityField.get(data);
                    if (value == null && ignoreNull) {
                        continue;
                    }
                    if (first) {
                        first = false;
                    } else {
                        sql.append(COMMA);
                    }
                    appendValue(values, value, insertFields, entityField, sql, placeHolder, index, driver);
                }
            }
            ++index;
        }
        sql.append(") VALUES (");
        first = true;
        for (int i = 0; i < index; ++i) {
            String value = placeHolder[i];
            if (value == null)
                continue;
            if (first) {
                first = false;
            } else {
                sql.append(",");
            }
            sql.append(value);
        }
        sql.append(')');

        //在insert的时候，需要判断一下null值是否可以入库
        validateValues(entity, builder);


    }

    private static void validateValues(Entity entity, SimpleSqlBuilder builder) {
        Set<EntityField> set = CollectionUtils.asSet(entity.getEntityFields());
        for (Object entityField : builder.adapters) {
            set.remove(entityField);
        }
        if (set.size() > 0) {

            for (EntityField entityField : set) {
                validateValue(entityField, null);
            }
        }
    }

    // 对于一个Bean来说，应该有相同的sql语句,假如有filter,则只插入null即可
    public static void buildInsert(
            SimpleSqlBuilder builder,
            SqlDriver driver,
            Entity<?> entity,
            Object data,
            Filter<EntityField> filter,
            boolean ignoreNull
    ) {

        StringBuilder sql = builder.sql;
        List<StatementAdapter> insertFields = builder.adapters;
        List<Object> values = builder.values;
        EntityField[] fields = entity.getEntityFields();
        sql.append("INSERT INTO ").append(
                driver.protectTable(entity.getTable())
        ).append(" (");
        boolean first = true;
        int index = 0;
        String[] placeHolder = new String[fields.length];
        // 计算占位符是什么，有可能是? ,也有可能是如 TO_DATE('YYYYMMDD',?)之类的函数调用
        // 也有可能直接是一个值，不需要占位符，如XXX.nextval(),有占位符，则需要值，没有占位符，则不需要值
        for (EntityField entityField : fields) {
            //插入数据,如果有忽略掉其他判断
            AutoField autoField = entityField.getAutoField();
            Object value;
            if (autoField != null) {
                String placeHolderValue;
                if ((placeHolderValue = autoField.getInsertPlaceHolder(data, entityField)) != null) {
                    placeHolder[index] = placeHolderValue;
                } else {
                    // 都没有？ 不需要处理
                    continue;
                }
            } else {
                if (filter == null || filter.accept(entityField)) {
                    value = entityField.get(data);
                    if (value == null && ignoreNull) {
                        continue;
                    }
                    if (first) {
                        first = false;
                    } else {
                        sql.append(COMMA);
                    }
                    appendValue(values, value, insertFields, entityField, sql, placeHolder, index, driver);
                }
            }
            ++index;
        }
        sql.append(") VALUES (");
        first = true;
        for (int i = 0; i < index; ++i) {
            String value = placeHolder[i];
            if (value == null)
                continue;
            if (first) {
                first = false;
            } else {
                sql.append(",");
            }
            sql.append(value);
        }
        sql.append(')');

        //在insert的时候，需要判断一下null值是否可以入库
        validateValues(entity, builder);

    }

    public static void buildUpdate(
            SimpleSqlBuilder builder,
            SqlDriver driver,
            Entity entity,
            Object record,
            Filter<EntityField> filter,
            boolean ignoreNull
    ) {
        StringBuilder sql = builder.sql;
        List<Object> values = builder.values;
        StringBuilder where = builder.where;

        List<StatementAdapter> adapters = builder.adapters;

        sql.append("UPDATE ");
        driver.protectTable(sql, entity.getTable());
        boolean first = true;
        int index = 0;
        for (EntityField field : entity.getEntityFields()) {
            if (filter == null || filter.accept(field)) {
                Object value = field.get(record);
                if (value == null && ignoreNull) continue;
                if (first) {
                    first = false;
                    sql.append(" SET ");
                } else {
                    sql.append(COMMA);
                }
                appendValue(values, adapters, field, index, value);
                ++index;
                driver.protectColumn(sql, field.getColumnName()).append("=?");
            }
        }
        if (index == 0) {
            throw new DaoException("至少更新一个字段");
        }
        if (where.length() > 0) {
            sql.append(" where ").append(where);
        } else {
            throw new DaoException("至少需要一个where条件");
        }

    }

    /**
     * 在update的时候进行值的判断需要注意的是：
     * 只有在有值，并且这个值为null的情况下才会判断空
     *
     * @param values
     * @param adapters
     * @param field
     * @param index
     * @param value
     */
    protected static void appendValue(
            List<Object> values,
            List<StatementAdapter> adapters,
            EntityField field,
            int index,
            Object value) {
        validateValue(field, value);
        values.add(index, value);
        adapters.add(index, field.getStatementAdapter());


    }

    public static void entityCondition(SimpleSqlBuilder builder, Entity entity, Object data) {
        // 主键
        for (EntityField adapter : entity.getPrimaryKeys()) {
            Object value = adapter.get(data);
            if (value != null) {
                builder.where(adapter.getColumnName(), value);
            }
        }
    }

    static void buildSelect(
            SimpleSqlBuilder builder,
            Entity entity,
            Filter<EntityField> filter,
            List<EntityField> entityFields) {
        // build select
        if (!entityFields.isEmpty()) {
            for (EntityField field : entityFields) {
                builder.selectRaw(field.getSelectColumnName());
            }
            return;
        }
        for (EntityField field : entity.getEntityFields()) {
            if (filter == null || filter.accept(field)) {
                builder.selectRaw(field.getSelectColumnName());
                entityFields.add(field);
            }
        }
        if (entityFields.isEmpty()) {
            throw new DaoException("必须指定至少一个select字段");
        }
    }


    static class PatterFilter implements Filter<EntityField> {

        private final Filter<String> pattern;

        PatterFilter(Filter<String> pattern) {
            this.pattern = pattern;
        }

        @Override
        public boolean accept(EntityField value) {
            return pattern.accept(value.getFieldName());
        }
    }
}
